#include <pio.h>
#include <system.h>
#include <std.h>
#include <error.h>

#define CMOS 0x70
#define CMOS_RD 0x71
#define CMOS_GET_SEC 0x0
#define PIC1 0x20
#define PIC2 0xa0
#define PIC_INIT 0x11
#define PIC1_REG 0x21
#define PIC2_REG 0xa1
#define DELAY_PRT 0x80


#define IRQMAP1 0x20
#define IRQMAP2 0x28

#define RMIRQ1 0x8
#define RMIRQ2 0x70

/* IRQ map:
PIC1:
tim		kbd		pic		sp2		sp1		pp2		dsk		pp1
0		0		1		1		1		1		1		1
0xFC
PIC2:
rtc		cga		RES		RES		RES		fpu		hdc		RES
1		1		1		1		1		1		0		1
0xBF
*/

#define MASKALL 0xff
#define ALLOWALL 0

#define PIC1PORT 0x21
#define PIC2PORT 0xa1

void write_pic1(unsigned char val)
{
	outb(PIC1PORT,val);
}

void write_pic2(unsigned char val)
{
	outb(PIC2PORT,val);
}

static inline unsigned char read_pic1()
{
	return inb(PIC1PORT);
}

static inline unsigned char read_pic2()
{
	return inb(PIC2PORT);
}

enum {statusSAVE,statusRESTORE};
char *irqerr="IRQ error";

static struct irq_st	{
	unsigned char mask1;
	unsigned char mask2;
	int status;
	int proc_flag;
}irq_st;

static void set_irq(unsigned char mask_register1,unsigned char mask_register2)
{                          
	write_pic1(mask_register1);
	write_pic2(mask_register2);
}

static void irq_remap(unsigned char irq_start,unsigned char irq_start2)
{
    outb(PIC1, PIC_INIT);
    outb(PIC2, PIC_INIT);
    outb(PIC1_REG, irq_start);
    outb(PIC2_REG, irq_start2);
    outb(PIC1_REG, 0x04);
    outb(PIC2_REG, 0x02);
    outb(PIC1_REG, 0x01);
    outb(PIC2_REG, 0x01);
    outb(PIC1_REG, 0x0);
    outb(PIC2_REG, 0x0);
}

int restore_irq_pm()
{
	irq_remap(IRQMAP1,IRQMAP2);
	set_irq(irq_st.mask1,irq_st.mask2);
}

int prepare_irq_rm()
{
	irq_remap(RMIRQ1,RMIRQ2);
}

int enable_irq(int irq_number)
{
	unsigned char pic;
	spin_lock irq;
	acquire_spin_lock(&irq);
	if(irq_number<8)
	{
		pic=inportb(0x21);
		pic&=(0xff-pow(2,irq_number));
		irq_st.mask1=pic;
		write_pic1(pic);
	}
	else
	{
		pic=inportb(0xa1);
		pic&=(0xff-pow(2,(irq_number-8)));
		irq_st.mask2=pic;
		write_pic2(pic);
	}
	release_spin_lock(&irq);
}

int disable_irq(int irq_number)
{
	
	unsigned char pic;
	spin_lock irq;
	acquire_spin_lock(&irq);
	if(irq_number<8)
	{
		pic=inportb(0x21);
		pic|=(pow(2,irq_number));
		irq_st.mask1=pic;
		write_pic1(pic);
	}
	else
	{
		pic=inportb(0xa1);
		pic|=(pow(2,(irq_number-8)));
		irq_st.mask2=pic;
		write_pic2(pic);
	}
	release_spin_lock(&irq);
}

int is_irq(int irq_number)
{
	unsigned char pic;
	spin_lock irq;
	acquire_spin_lock(&irq);
	if(irq_number<8)
	{
		pic=inportb(0x21);
		return (pic&(pow(2,irq_number)));
	}
	else
	{
		pic=inportb(0xa1);
		return (pic&(pow(2,(irq_number-8))));
	}
	release_spin_lock(&irq);
}

int irq_ended(int irq_number)
{
	if(irq_number<8)	{
		outb(0x20,0x20);
	}
	else	{
		outb(0xa0,0x20);
		outb(0x20,0x20);
	}
	return NULL;
}

static int irq_save()
{
	if(irq_st.mask1==statusRESTORE)	{
		irq_st.mask1=inportb(0x21);
		irq_st.mask2=inportb(0xa1);
		irq_st.mask1=statusSAVE;
	}
	else
		i_error(irqerr);
	return 0;
}

static int irq_restore()
{
	if(irq_st.mask1==statusSAVE)	{
		write_pic1(irq_st.mask1);
		write_pic2(irq_st.mask2);
		irq_st.mask1=statusRESTORE;
	}
	else
		i_error(irqerr);
	return 0;
}

int irqs_enabled()
{
	int mask1,mask2;
	mask1=inportb(0x21);
	mask2=inportb(0xa1);
	if(mask1==0xff && mask2==0xff)
		return 1;
	return NULL;
}

int irq_save_disable()
{
	spin_lock nirq;
	acquire_spin_lock(&nirq);
	irq_save();
	set_irq(MASKALL,MASKALL);
	release_spin_lock(&nirq);
	return NULL;
}

int irq_restore_enable()
{
	return irq_restore();
}

int start_mono()
{
	cli();
	irq_st.proc_flag++;
	return SUCCESS;
}

int release_mono()
{
	irq_st.proc_flag--;
	if(irq_st.proc_flag<0)	{
		i_error("release_mono:No mono");
	}
	else if(irq_st.proc_flag>0 && interrupt_flag())	{
		i_error("release_mono:Mono released prematurely");
	}
	if(irq_st.proc_flag==0)	{
		sti();
	}
	return SUCCESS;
}		

static int init_Done=0;
int init_8259()
{
	if(init_Done==8259)	{
		return -1;
	}
	irq_remap(IRQMAP1,IRQMAP2);
	irq_st.mask1=MASKALL;
	irq_st.mask2=MASKALL;
	irq_st.proc_flag=0;
	set_irq(irq_st.mask1,irq_st.mask2);
	set_all_irqs();	//This asm function sets all hw ints to uirq#
	enable_irq(2);
	irq_st.status=statusRESTORE;
	init_Done=8259;
	sti();
}
